#include "Sock.hpp"
#include <sys/select.h>

#define NUM (sizeof(fd_set) * 8)

int fd_array[NUM]; // -1 表示没有存储fd，>=表示有fd

static void Usage(std::string proc)
{
  std::cout << "Usage: " << proc << " port" << std::endl;
}


// ./select_server port
int main(int argc, char* argv[])
{
  if(argc != 2)
  {
    Usage(argv[0]);
    exit(1);
  }
  
  int listen_sockfd = Sock::Socket();
  Sock::Bind(listen_sockfd, atoi(argv[1]));
  Sock::Listen(listen_sockfd);

  // 使用多路转接
  // 初始化
  for(int i = 0; i < NUM; ++i)
  {
    fd_array[i] = -1;
  }
  fd_array[0] = listen_sockfd;

  fd_set read_fd;
  FD_SET(listen_sockfd, &read_fd);
  while(true)
  {
    // 初始化read_fd,并找出max_fd
    FD_ZERO(&read_fd);
    int max_fd = listen_sockfd;
    for(int i = 0; i < NUM; ++i)
    {
      if(fd_array[i] != -1)
      {
        FD_SET(fd_array[i], &read_fd); 
      }
      if(fd_array[i] > max_fd)
      {
        max_fd = fd_array[i];
      }
    }

    /* 
     * 阻塞等待 timeout = nullptr;
     * 非阻塞等待 timeout = {0, 0};
     * timeout = {5, 10};
     * 在5010ms前阻塞等待,在5010ms后非阻塞等待
     * 返回值：
     * 1、如果阻塞等待成功,返回剩余时间
     * 2、如果非阻塞,返回时间0
     * 3、如果错误,返回-1
    */
    //struct timeval timeout = {0, 0};
    struct timeval timeout = {10, 0};
    int ret = select(max_fd + 1, &read_fd, nullptr, nullptr, &timeout);
    switch(ret)
    {
      case 0: 
        //std::cout << "return val: " << ret << std::endl;
        // 等待超时
        fprintf(stdout, "select timeout!\n");
        sleep(2);
        break;
      case -1:
        std::cout << "return val: " << ret << std::endl;
        // 错误
        fprintf(stderr, "select: %s\n", strerror(errno));
        break;
      default:
        //std::cout << "return val: " << ret << std::endl;
        //fprintf(stdout, "有对应的事件发送！\n");
        // 读取成功
        for(int i = 0; i < NUM; ++i)
        {
          // 当sockfd为监听套接字时
          if(FD_ISSET(fd_array[i], &read_fd))
          {
            if(fd_array[i] == listen_sockfd)
            {
              //fprintf(stdout, "新链接为监听套接字\n");
              int fd = Sock::Accept(listen_sockfd);  
              // 将新的fd添加到fd_array中
              int pos = 0;
              for(; pos < NUM; ++pos)
              {
                if(fd_array[pos] == -1) break;
              }
                
              if(pos == NUM)
              {
                //fprintf(stderr, "服务器已经满载了！\n");
                close(fd);
              }
              else
              {
                fd_array[pos] = fd;
                //fprintf(stdout, "成功添加新链接client#[4]\n", fd);
              }
            }
            else
            {
              //fprintf(stdout, "新链接为普通文件描述符client[%d]\n", fd_array[i]);
              // 普通文件描述符
              char buffer[NUM];
              ssize_t s = read(fd_array[i], buffer, sizeof(buffer) - 1);
              if(s > 0)
              {
                //fprintf(stdout, "成功读取数据！\n");
                // 读取成功
                buffer[s - 1] = 0;
                fprintf(stdout, "client[%d]# %s\n", fd_array[i], buffer);
              }
              else if(s == 0)
              {
                // 对端关闭链接
                fprintf(stdout, "对方关闭链接！\n");
                close(fd_array[i]);
                fd_array[i] = -1;
              }
              else
              {
                // 读取错误
                fprintf(stderr, "read: %s\n", strerror(errno));
                close(fd_array[i]);
                fd_array[i] = -1;
              }
            }
          }
        }
    }
  }
  return 0;
}

// ./select_server port
//int main(int argc, char* argv[])
//{
//  if(argc != 2)
//  {
//    Usage(argv[0]);
//    exit(1);
//  }
//  
//    int listen_sockfd = Sock::Socket();
//    Sock::Bind(listen_sockfd, atoi(argv[1]));
//    Sock::Listen(listen_sockfd);
//    int sockfd = Sock::Accept(listen_sockfd);
//
//  while(true){
//    char buffer[1024];
//    ssize_t s = read(sockfd, buffer, sizeof(buffer));
//    if(s > 0)
//    {
//      buffer[s - 1] = '\0';
//      fprintf(stdout, "client# %s\n", buffer);
//    }
//    else if(s == 0){
//      fprintf(stdout, "对方关闭链接\n");
//      close(sockfd);
//      break;
//    }
//    else
//    {
//      fprintf(stderr, "read: %s\n", strerror(errno));
//      close(sockfd);
//      break;
//    }
//  }
//  return 0;
//}
